//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

#include <maya/MFnStringData.h>
#include <maya/MFnMatrixData.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MGlobal.h>
#include <maya/MDGModifier.h>
#include <maya/MImage.h>
#include <maya/MPlugArray.h>
#include <maya/MFileIO.h>
#include <maya/MMatrix.h>
#include <maya/MFnMesh.h>
#include <maya/MString.h>
#include <maya/MStringResource.h>
#include <maya/MStringResourceId.h>

#include <sys/types.h>
#include <sys/stat.h>

#include <algorithm>

#include "Platform.h"

#include "GLSLShaderNode.h"
#include "AshliShaderStrings.h"
#include "glExtensions.h"

#include "ResourceManager.h"

#include <maya/MHWShaderSwatchGenerator.h>
#include <maya/MHardwareRenderer.h>
#include <maya/MGeometryData.h>
#include <maya/MImage.h>

// If and when a bug in Maya is fixed so that you 
// can keyframe internal dynamic attributes, the
// code within this ifdef cannot be used.
// If you set the attributes to be internal, you
// can keyframe, but playback may or may not work
// erratically. This is a known API bug.
//
//#define _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
//

//
//statics
//
////////////////////////////////////////////////////////////////////////////////

// This id is supplied by Maya and should never be changed.
MTypeId glslShaderNode::sId( 0x81022);

//these are the attributes shared by all instances
MObject	glslShaderNode::sShader;
MObject	glslShaderNode::sTechnique;
MObject glslShaderNode::sTechniqueList; // Convenience attribute
MObject glslShaderNode::sShaderPath; // Convenience attribute
MObject glslShaderNode::sNakedTexCoords[8];
MObject glslShaderNode::sNakedColors[2];

//this is the default shader, used to render a checkered phong
shader *glslShaderNode::sDefShader = NULL;

//  This is a list of nodes presently created of this type.
// It allows us to find all shader nodes and update them after a file load,
// and it eases searching for use by the command.
std::vector<glslShaderNode*> glslShaderNode::sNodeList;

//default texture map strings
//const char *glslShaderNode::nakedSetNames[8] = { "map1", "", "", "", "", "", "", ""};
const char *glslShaderNode::nakedSetNames[8] = { "map1", "tangent", "binormal", "", "", "", "", ""};

//
//
//
////////////////////////////////////////////////////////////////////////////////
glslShaderNode::glslShaderNode() : m_shader(NULL), m_shaderDirty(false), m_parsedUserAttribList( false) {

	// Make sure we have our extensions initialised
	// We do it here rather than at render time so that setting a shader program
	// in batch mode will populate the shader attributes
	//
	InitializeExtensions();

  for (int ii=0; ii<8; ii++) {
    m_nakedTexSets[ii] = nakedSetNames[ii];
    m_setNums[ii] = -1; 
	m_mayaType[ii] = kNonMaya; 
  }
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
glslShaderNode::~glslShaderNode() {

	if (MGlobal::mayaState() != MGlobal::kBatch)
	{
		MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
		if (pRenderer)
		{
			const MString & backEndStr = pRenderer->backEndString();
			MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
			if (status != MStatus::kSuccess) {
				MStatus strStat;
				MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
				MGlobal::displayError(status.errorString());
			}
		}
	}

  //clean up any textures we have created
  for (std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin(); it<m_samplerAttribList.end(); it++) {
    ResourceManager::destroyTexture( it->texName);
  }

  //keep the list of allocated shaders correct
  { //this fixes MSVC and its busted scoping rules

    std::vector<glslShaderNode*>::iterator it = std::find( sNodeList.begin(), sNodeList.end(), this);
    if (it != sNodeList.end()) {
      //remove this one from the list
      sNodeList.erase(it);
    }
    else {
      //this is really bad, somehow one got created without going through create
    }
  }

  delete m_shader;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::postConstructor() {
}

//
//compute when DAG change occurs (likely nothing for HwShader)
//
////////////////////////////////////////////////////////////////////////////////
MStatus glslShaderNode::compute( const MPlug& plug, MDataBlock& data) {

  //need to look to compute a default color
  
  if ((plug == outColor) || (plug.parent() == outColor))
  {
    MFloatVector color(.95f, .1f, .07f);
    MDataHandle outputHandle = data.outputValue( outColor );
    outputHandle.asFloatVector() = color;
    outputHandle.setClean();
    return MS::kSuccess;
  }

  return MS::kUnknownParameter;
}

//
//factory for node creation
//
////////////////////////////////////////////////////////////////////////////////
void* glslShaderNode::creator() {
  glslShaderNode *ret = new glslShaderNode;

  //add this new node to the list
  sNodeList.push_back(ret);

  return ret;
}

//
// initialization (static)
//
////////////////////////////////////////////////////////////////////////////////
MStatus glslShaderNode::initialize() {

  //add the necessary nodes
  MFnTypedAttribute typedAttr;
  MFnStringData stringData;
  MStatus stat;
  MObject string;

  //  The attribute names and short names below are selected to be compatible with
  // the CgFX plugin for simplicity


  //
  // Create the shader attribute and add it to the node type
  //

  // The shader attribute holds the file name of an fx file
  string = stringData.create(&stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  sShader = typedAttr.create("shader", "s", MFnData::kString, string, &stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  stat = typedAttr.setInternal(true);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  // Add the attribute to the node
  if ( addAttribute(sShader) != MS::kSuccess)
    return MS::kFailure;

  //
  // Create the technique 
  //
  
  // The technique attribute holds the currently selected technique name
  string = stringData.create(&stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  sTechnique = typedAttr.create("technique", "t", MFnData::kString, string, &stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  stat = typedAttr.setInternal(true);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  // Add the attribute to the node
  if ( addAttribute(sTechnique) != MS::kSuccess)
    return MS::kFailure;


  // Create a techniqueList to allow access by the AE for UI purposes
  sTechniqueList = typedAttr.create("techniqueList", "tl", MFnData::kString, string, &stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  stat = typedAttr.setInternal(true);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  typedAttr.setWritable( false );
  typedAttr.setStorable( false );
  typedAttr.setHidden( true );
  typedAttr.setConnectable( false );

  // Add the attribute to the node
  if ( addAttribute(sTechniqueList) != MS::kSuccess)
    return MS::kFailure;

  // Create a shader path attrib to allow access by the AE for UI purposes
  sShaderPath = typedAttr.create("shaderPath", "sp", MFnData::kString, string, &stat);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  stat = typedAttr.setInternal(true);
  if (stat != MS::kSuccess)
    return MS::kFailure;

  typedAttr.setWritable( false );
  typedAttr.setStorable( false );
  typedAttr.setHidden( true );
  typedAttr.setConnectable( false );  

  // Add the attribute to the node
  if ( addAttribute(sShaderPath) != MS::kSuccess)
    return MS::kFailure;

  int ii;

  //
  // Add the naked texture coord sets to the node
  //
  for (ii=0; ii<8; ii++) {
    string = stringData.create( MString(nakedSetNames[ii]));
    sNakedTexCoords[ii] = typedAttr.create( MString("TexCoord") + ii, MString("t")+ ii, MFnData::kString, string);
    typedAttr.setInternal( true);
    typedAttr.setKeyable( true );
    addAttribute( sNakedTexCoords[ii]);
  }

  // Add the naked color sets to the node
  for (ii=0; ii<2; ii++) {
    sNakedColors[ii] = typedAttr.create( MString("Color") + ii, MString("c")+ ii, MFnData::kString);
    typedAttr.setInternal( true);
    typedAttr.setKeyable( true );
    addAttribute( sNakedColors[ii]);
  }

  return MS::kSuccess;
}

//
// callback to configure shaders, post file load
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::rejigShaders( void *data) {

  //iterate over all shaders, and rebuild them, so they can reconnect attributes
  for (std::vector<glslShaderNode*>::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) {
    (*it)->rebuildShader();
  }
}

//
// Update shader and attributes
//
////////////////////////////////////////////////////////////////////////////////
static bool textureInitPowerOfTwo(unsigned int val, unsigned int & retval)
{
	unsigned int res = 0;				// May be we should return 1 when val == 0
	if (val)
	{
		// Muliply all values by 2, to simplify the testing:
		// 3*(res/2) < val*2 <= 3*res
		val <<= 1;
		unsigned int low = 3;
		res = 1;
		while (val > low)
		{
			low <<= 1;
			res <<= 1;
		}
	}

	retval = res;
    return (res == (val>>1)) ? 1 : 0;
}

bool glslShaderNode::rebuildShader()
{
  //handle shader initialization
  
	bool rebuiltAnything = false;

	GL_CHECK;

	// Re (build) current shader
	if (m_shaderDirty) {

		MGlobal::displayInfo(MString("Rebuild shader : ") + m_shaderPath.asChar() );

		// Clean up old shader (delete NULL is safe, does not need protected)
		delete m_shader;
        GL_CHECK;

		// Create a new shader
        m_shader = shader::create(m_shaderPath.asChar());
        GL_CHECK;

		if (!m_shader) {
			MStatus strStat;
			MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedLoadShader, strStat));
            MGlobal::displayError(shader::sError.c_str());
		}
		else {
			rebuiltAnything = true;


			// Try to reuse the existing technique name.
			// This will allow for being able to re-read
			// files which have saved a technique name which
			// is not technique 0.
			bool foundExistingTechnique = false;
			if (m_techniqueName.length())
			{
				for (int ii=0; ii<m_shader->techniqueCount(); ii++) {
					if (m_techniqueName  == m_shader->techniqueName(ii)) {
						m_shader->setTechnique(ii);
						foundExistingTechnique = true;
						break;
					}
				}
			}
			// Just use technique 0 as a default, since we don't 
			// know which one to use.
			if (!foundExistingTechnique)
			{
				m_techniqueName = m_shader->techniqueName(0);
			}

			m_techniqueList = m_techniqueName ;
			for (int ii=1; ii<m_shader->techniqueCount(); ii++)
				m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii);

			// Reconfigure node attributes
			configureAttribs();
            GL_CHECK;
		}
		m_shaderDirty = false;
	}

    m_techniqueList = "";
	if (m_shader && m_shader->techniqueCount())
	{
		for (int ii=0; ii<m_shader->techniqueCount(); ii++)
			m_techniqueList = m_techniqueList + " " + m_shader->techniqueName(ii);
	}

	return rebuiltAnything;
}

//
// search the dependency graph for a file texture node
//
////////////////////////////////////////////////////////////////////////////////
MObject glslShaderNode::findFileTextureNode( const MPlug &plug) {
  MPlugArray arr;
  MStatus stat;
  plug.connectedTo(arr, true, false, &stat);
  MObject srcNode;

  if (stat != MStatus::kSuccess)
    return MObject::kNullObj;

  if (arr.length() == 1)
    srcNode = arr[0].node();
  else if (arr.length() == 0 ) {
    //plug is not connected
    return MObject::kNullObj;
  }
  else {
    //plug is multiply connected
    MGlobal::displayWarning("Texture plug is connected to multiple nodes");
    return MObject::kNullObj;
  }
  
  if( srcNode != MObject::kNullObj) {
    MStatus rc;
    MFnDependencyNode dgFn( srcNode, &rc);
    MPlug fnPlug = dgFn.findPlug( "fileTextureName", &rc);
    if (rc == MStatus::kSuccess) {
      MStatus tStat;
      MObject fTex = fnPlug.node(&tStat);
      if ( tStat != MStatus::kSuccess)
        return MObject::kNullObj;
      else
        return fTex;
    }
  }
  return MObject::kNullObj;
}

//
// search the dependency graph for an envCube node
//
////////////////////////////////////////////////////////////////////////////////
MObject glslShaderNode::findEnvCubeNode( const MPlug &plug) {
  MPlugArray arr;
  plug.connectedTo(arr, true, false);
  MObject srcNode;

  if (arr.length() == 1)
    srcNode = arr[0].node();
  else if (arr.length() == 0 ) {
    //plug is not connected
    return MObject::kNullObj;
  }
  else {
    //plug is multiply connected
    MGlobal::displayWarning("Texture plug is connected to multiple nodes");
    return MObject::kNullObj;
  }
  
  // if we have a real node, look for the envCube
  if( srcNode != MObject::kNullObj) {
    MStatus rc;
    MFnDependencyNode dgFn( srcNode, &rc);

    // searching for the plug seems a pretty good solution
    MPlug fnPlug = dgFn.findPlug( "left", &rc);

    if (rc == MStatus::kSuccess) {
      MStatus tStat;
      MObject fCube = fnPlug.node(&tStat);
      if ( tStat != MStatus::kSuccess)
        return MObject::kNullObj;
      else
        return fCube;
    }
  }
  return MObject::kNullObj;
}

//
// load a cubemap face from a cubeEnv node
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::loadCubeFace( glslShaderNode::CubeFace cf, MObject &cube, MFnDependencyNode &dn){
  const char* faceNames[] = { "left", "right", "top", "bottom", "front", "back"};
  const GLenum targets[] = {GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
    GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
    GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
  float rgb[3];

  MObject face = dn.attribute( faceNames[cf]);
  MPlug plug( cube, face);
  MObject fTex = findFileTextureNode( plug);

  if (fTex != MObject::kNullObj) {
	  MImage img;
	  unsigned int width, height;

	  img.readFromTextureNode(fTex);
	  MStatus status = img.getSize( width, height);

	  // If not power of two and NPOT is not supported, then we need 
	  // to resize the original system pixmap before binding.
	  //
	  // Also handle non-square cube map textures
	  bool resizeToBeSquare = false;
	  if (width != height)
	  {
		  width = height;
		  resizeToBeSquare = true;
	  }
	  if (width > 0 && height > 0 && (status != MStatus::kFailure) )
	  {
		  // If not power of two and NPOT is not supported, then we need 
		  // to resize the original system pixmap before binding.
		  //
		  if (!glTextureNonPowerOfTwo || resizeToBeSquare )
		  {
			  if (width > 2 && height > 2)
			  {
				  unsigned int p2Width, p2Height;
				  bool widthPowerOfTwo  = textureInitPowerOfTwo(width,  p2Width);
				  bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height);
				  if(!widthPowerOfTwo || !heightPowerOfTwo || resizeToBeSquare)
				  {
					  width = p2Width;
					  height = p2Height;
					  img.resize( p2Width, p2Height, false /* preserverAspectRatio */);
				  }
			  }
		  }

		  //MGlobal::displayInfo( MString("Loading ") + faceNames[cf]+ " face (" + width + "," + height + ")");
		  glTexImage2D( targets[cf], 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
		  GL_CHECK;
	  }
  }
  else {
    MObject data;
    plug.getValue( data);
    MFnNumericData val(data);
    val.getData( rgb[0], rgb[1], rgb[2]);
    glTexImage2D( targets[cf], 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb);
  }
}

//
//  Bind data such as texture objects for samplers.
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::BindSamplerData() {

  //iterate over all samplers
  for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {

    //only update if it has been marked dirty
    if (it->dirty) {
      it->dirty = false;

      //get the plug and associated info
      MPlug plug(thisMObject(), it->attrib);
      MObject data;
      plug.getValue(data);
      MFnNumericData val(data);
      float rgb[3];

      if (it->texName == 0) {
        //need to gen a new name, one has not been created
        glGenTextures( 1, &(it->texName));
        m_shader->updateSampler( it->pNum, it->texName);
        GL_CHECK;
      }

      switch (m_shader->samplerType(it->pNum) ) {

        case shader::st1D:
          //just load a 1D texture from the plug value until we handle files directly
          val.getData( rgb[0], rgb[1], rgb[2]);
          glBindTexture( GL_TEXTURE_1D, it->texName);
          glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, it->minFilter);
          glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, it->magFilter);
          glTexParameteri( GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, it->wrapMode);
          glTexParameterf( GL_TEXTURE_1D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
          if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
            glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_TRUE);
          }
          else {
            glTexParameteri( GL_TEXTURE_1D, GL_GENERATE_MIPMAP, GL_FALSE);
          }
          glTexImage1D( GL_TEXTURE_1D, 0, GL_RGB8, 1, 0, GL_RGB, GL_FLOAT, rgb);
          GL_CHECK;
          break;

        case shader::st1DShadow:
          //are shadow maps accessible in Maya?
          break;

        case shader::st2D:
          {
            bool done = false;
            //set up the texture stuff
            glBindTexture( GL_TEXTURE_2D, it->texName);
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, it->minFilter);
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, it->magFilter);
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, it->wrapMode);
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, it->wrapMode);
            glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
            if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
              glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
            }
            else {
              glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
            }
            MObject fTex = findFileTextureNode( plug);

			if (fTex != MObject::kNullObj) {
				MImage img;
				unsigned int width, height;

				img.readFromTextureNode(fTex);
				MStatus status = img.getSize( width, height);
			    if (width > 0 && height > 0 && (status != MStatus::kFailure) )
				{
					// If not power of two and NPOT is not supported, then we need 
					// to resize the original system pixmap before binding.
					//
					if (!glTextureNonPowerOfTwo)
					{
						if (width > 2 && height > 2)
						{
							unsigned int p2Width, p2Height;
							bool widthPowerOfTwo  = textureInitPowerOfTwo(width,  p2Width);
							bool heightPowerOfTwo = textureInitPowerOfTwo(height, p2Height);
							if(!widthPowerOfTwo || !heightPowerOfTwo)
							{
								width = p2Width;
								height = p2Height;
								img.resize( p2Width, p2Height, false /* preserverAspectRatio */);
							}
						}
					}
					glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.pixels());
					GL_CHECK;
					done = true;
				}
			}
            
            if (!done) {
              //just read it from the plug
              val.getData( rgb[0], rgb[1], rgb[2]);
              glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_FLOAT, rgb);
            }
            GL_CHECK;
          }
          break;

        case shader::stCube:
          {
            //search for envCube node, then traverse off the 6 faces
            glBindTexture( GL_TEXTURE_CUBE_MAP, it->texName);
            glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, it->minFilter);
            glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, it->magFilter);
            glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, it->wrapMode);
            glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, it->wrapMode);
            glTexParameterf( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, it->maxAniso);
            if ( (it->minFilter == GL_LINEAR_MIPMAP_NEAREST) || (it->minFilter == GL_LINEAR_MIPMAP_LINEAR)) {
              glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE);
            }
            else {
              glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_FALSE);
            }
            MObject cube = findEnvCubeNode( plug);
            if ( cube != MObject::kNullObj) {
              MFnDependencyNode dn(cube);
              loadCubeFace( cfLeft, cube, dn);
              loadCubeFace( cfRight, cube, dn);
              loadCubeFace( cfTop, cube, dn);
              loadCubeFace( cfBottom, cube, dn);
              loadCubeFace( cfFront, cube, dn);
              loadCubeFace( cfBack, cube, dn);
            }
          }
          break;

        case shader::st2DShadow:
        case shader::st3D:
          //need to handle file loading directly for 3D textures
          break;
		default:
		  break;
      };
    }
  }
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::BindUniformData() {

  for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) {

	// [ANIM_UNIFORM_FIX2]
	// To allow animation to work, uniform data has been
	// left to be dirty all of the time, as internal attributes
    // do not update properly during playback.
	//
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
    if (it->dirty) {
#endif
      it->dirty = false;

      MPlug plug(thisMObject(), it->attrib);
      MObject data;
      plug.getValue(data);
      MFnNumericData val(data);
      float fTemp[4];
      int iTemp[4];
      bool bTemp[4];

      switch (m_shader->uniformType(it->pNum)) {

        case shader::dtBool:
          plug.getValue(bTemp[0]);
          m_shader->updateUniformBool( it->pNum, bTemp[0]);
          break;

        case shader::dtBVec2:
        case shader::dtBVec3:
        case shader::dtBVec4:
          //no Maya native type, these are unsupported for now
          break;

        case shader::dtInt:
          plug.getValue(iTemp[0]);
          m_shader->updateUniformInt( it->pNum, iTemp[0]);
          break;

        case shader::dtIVec2:
          val.getData( iTemp[0], iTemp[1]);
          m_shader->updateUniformIVec( it->pNum, iTemp);
          break;

        case shader::dtIVec3:
          val.getData( iTemp[0], iTemp[1], iTemp[2]);
          m_shader->updateUniformIVec( it->pNum, iTemp);
          break;

        case shader::dtIVec4:
          //no Maya native type, these are unsupported for now
          break;

        case shader::dtFloat:
          plug.getValue(fTemp[0]);
          m_shader->updateUniformFloat( it->pNum, fTemp[0]);
          break;

        case shader::dtVec2:
          val.getData( fTemp[0], fTemp[1]);
          m_shader->updateUniformVec( it->pNum, fTemp);
          break;

        case shader::dtVec3:
          val.getData( fTemp[0], fTemp[1], fTemp[2]);
          m_shader->updateUniformVec( it->pNum, fTemp);
          break;

        case shader::dtVec4:
          {
            MPlug plug2(thisMObject(), it->attrib2);
            val.getData( fTemp[0], fTemp[1], fTemp[2]);
            plug2.getValue( fTemp[3]);
            m_shader->updateUniformVec( it->pNum, fTemp);
          }
          break;

        case shader::dtMat4:
          {
            MFnMatrixData mValue(data);
            MMatrix matrix = mValue.matrix();
            m_shader->updateUniformMat( it->pNum, (double*)matrix.matrix);
          }
          break;

        case shader::dtMat2:
        case shader::dtMat3:
        
          //unsupported for now, due to Maya's interface presently lacking support
          break;
		default:
		  break;
      };
    }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
  }
#endif
}


// Method to override to let Maya know this shader is batchable. 
//
bool glslShaderNode::supportsBatching() const
{
	return true;
}


//
// Old style drawing commands like the HW renderer needs
//
////////////////////////////////////////////////////////////////////////////////
MStatus glslShaderNode::glBind(const MDagPath& shapePath) {

    // Rebuild shader, if dirty
	// This is only required pre 7.0, and should not be part of
	// the plugin anymore unless we decide to ship with 6.5.
	//
  
  // Initialize GL extesions
  if (!InitializeExtensions()) {
	MStatus strStat;
    MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeAshliFailedFindExtensions, strStat));
    return MStatus::kFailure;
  }
    GL_CHECK;

    //attempt to clean up any delayed deletions
    ResourceManager::recover();

	// Build default shader
	if (!sDefShader)
		sDefShader = new defaultShader;
    GL_CHECK;

  //
  //bind any dirty data

  //should add an extra global dirty flag
  if (m_shader) {
    if (!m_shader->build())
      MGlobal::displayError(MString(m_shader->errorString()));

    BindUniformData();

    //now the samplers
    BindSamplerData();
  }

  // Disable these to avoid possible problems with shader programs
  // going into software
  glDisable(GL_POINT_SMOOTH);
  glDisable( GL_LINE_SMOOTH);

  //check for active shader, if none, fall back to the default shader
  if ((m_shader != NULL) && (m_shader->valid())) 
  {
    m_passCount = m_shader->passCount();
    
    if (!m_shader->build()) {
      m_passCount = 0;
    }

	if( m_passCount == 1)
	{
		m_shader->setPass( 0);
		m_shader->bind();
	}
  }
  else
  {
    m_passCount = -1;
	m_maxSetNum = 1; // We need the default UV
	sDefShader->bind();
  }
	
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnable(GL_VERTEX_PROGRAM_TWO_SIDE);

  GL_CHECK;

  return MS::kSuccess;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
MStatus glslShaderNode::glUnbind(const MDagPath& shapePath) {

	for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) 
		glDisableVertexAttribArray( it->handle);

	for( int ii = 0; ii < m_maxSetNum; ii++) 
		m_glState.disableTexCoord( ii);
	m_glState.activeTexture( 0);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisable(GL_VERTEX_PROGRAM_TWO_SIDE);

	// If we're not using multi-pass, disable the effect here
	if( m_passCount < 0)
		sDefShader->unbind();
	else if( m_passCount == 1)
		m_shader->unbind();

  // Dirty our attribute list so it gets re-built next time
  // this shader is used
  m_parsedUserAttribList = false;

  return MS::kSuccess;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::updateBoundAttributes( const MDagPath& shapePath) {
  //
  // This code needs to get the current matrices
  //  the world matrix is easy (dagPath)
  //  The view matrix seems like it can come from the current M3DView
  //  I can't see any good way to get the projection matrix other than reading it
  //
  MMatrix world;
  MMatrix temp;
  MMatrix proj;
  MMatrix view;
  MMatrix worldview;

  //get the matrices
  if (shapePath.isValid())
	  world = shapePath.inclusiveMatrix();
  else
	  world.setToIdentity();
  glGetDoublev( GL_PROJECTION_MATRIX, (double*)proj.matrix);
  glGetDoublev( GL_MODELVIEW_MATRIX, (double*)worldview.matrix);
  view = world.inverse() * worldview;

  for (std::vector<boundUniform>::iterator it = m_boundUniformList.begin(); it < m_boundUniformList.end(); it++) {
    switch ( it->usage) {

      case shader::smWorld:
        m_shader->updateUniformMat( it->pNum, (double*)world.matrix);
        break;

      case shader::smView:
        m_shader->updateUniformMat( it->pNum, (double*)view.matrix);
        break;

      case shader::smProjection:
        m_shader->updateUniformMat( it->pNum, (double*)proj.matrix);
        break;

      case shader::smWorldView:
        m_shader->updateUniformMat( it->pNum, (double*)worldview.matrix);
        break;

      case shader::smViewProjection:
        temp = view * proj;
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewProjection:
        temp = worldview * proj;
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldI:
        temp = world.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewI:
        temp = view.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smProjectionI:
        temp = proj.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewI:
        temp = worldview.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewProjectionI:
        temp = view * proj;
        temp = temp.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewProjectionI:
        temp = worldview * proj;
        temp = temp.inverse();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldT:
        temp = world.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewT:
        temp = view.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smProjectionT:
        temp = proj.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewT:
        temp = worldview.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewProjectionT:
        temp = view * proj;
        temp = temp.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewProjectionT:
        temp = worldview * proj;
        temp = temp.transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldIT:
        temp = world.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewIT:
        temp = view.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smProjectionIT:
        temp = proj.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewIT:
        temp = worldview.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewProjectionIT:
        temp = view * proj;
        temp = temp.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smWorldViewProjectionIT:
        temp = worldview * proj;
        temp = temp.inverse().transpose();
        m_shader->updateUniformMat( it->pNum, (double*)temp.matrix);
        break;

      case shader::smViewPosition:
        {
          float vec[4];
          temp = view.inverse();

          vec[0] = (float)temp[3][0];
          vec[1] = (float)temp[3][1];
          vec[2] = (float)temp[3][2];
          vec[3] = (float)temp[3][3];

          m_shader->updateUniformVec( it->pNum, vec);
        }
        break;
      
      case shader::smTime:
        // need to find a good way to get the current animation time
        //  the cgfx plugin uses:
        //    (float)timeGetTime() * 1000.0f
        // This has two problems
        //  1. one it does not correspond to Maya time
        //  2. it has portability problems
        m_shader->updateUniformFloat( it->pNum, 0.0f);
        break;
      
      case shader::smViewportSize:
        {
          float vp[4];
          glGetFloatv( GL_VIEWPORT, vp);
          m_shader->updateUniformVec( it->pNum, &vp[2]);
        }
        break;

	  default:
		break;
    };
  }
}

//
//  Configure the texture coordinate sets and color sets for rendering. This
// function sets up all the vertex arry pointers or current values, if a set
// is not available.
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::configureTexCoords( int normalCount, const float ** normalArrays, 
										int texCoordCount,
                                        const float ** texCoordArrays,
										int colorCount,
										const float ** colorArrays) 
{

  //printf("*********** configureTexCoords [normal count = %d]\n", normalCount);
											
  int ii;

  //bind in the naked texture sets
  for (ii=0; ii<m_maxSetNum; ii++) {

    //handle the special numbers
    if (m_setNums[ii] == -1) {
      // no name is specified, send a default texture coord of (0,0)
      glMultiTexCoord2f( GL_TEXTURE0 + ii, 0.0f, 0.0f);
      m_glState.disableTexCoord( ii);
      continue;
    }
    else if (m_mayaType[ii] == kNormal)  {

      //this set has requested a normal
      if ( (normalCount > 0) && normalArrays[0]) {
        //there is a tangent
        m_glState.enableAndActivateTexCoord( ii);
        glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[0]);
      }
      else {
        //there is no tangent available
        glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 1.0f);
        m_glState.disableTexCoord( ii);
      }
      continue;
    }
    else if (m_mayaType[ii] == kTangent)  {

      //this set has requested a tangent
      if ( m_numNormals > 1  && normalCount > 1)
	  {
		  int offset = 0;
		  if (m_numNormals > 2)
			offset = ( m_setNums[ii] * 3 ) + 1;
		  else
			offset = ( m_setNums[ii] * 2 ) + 1;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 2)
				  offset = 1;
			  else
				  offset = -1;
		  }

		  //printf("TANGENT%d : Access tangents via collection id(%d) = offset %d\n",
		//		ii, m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset]) {
			  //there is a tangent
			  m_glState.enableAndActivateTexCoord( ii);
			  glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
		  }
		  else {
			  //there is no tangent available
			  glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f);
			  m_glState.disableTexCoord( ii);
		  }
	  }
	  else
	  {
		  //there is no tangent available
		  glMultiTexCoord3f( GL_TEXTURE0 + ii, 1.0f, 0.0f, 0.0f);
		  m_glState.disableTexCoord( ii);
	  }
      continue;
    }
    else if (m_mayaType[ii] == kBinormal)  {

      //this set has requested a binormal
      if ( m_numNormals > 2 && normalCount > 1)
	  {
		  int offset = ( m_setNums[ii] * 3 ) + 2;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 3)
				  offset = 2;
			  else
				  offset = -1;
		  }

		  //printf("BINORMAL%d : Access binormals via collection id(%d) = offset %d\n",
			//	ii, m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset] )
		  {
			  //there is a binormal
			  m_glState.enableAndActivateTexCoord( ii);
			  glTexCoordPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
		  }
		  else
		  {
			  //there is no binormal available
			  glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
			  m_glState.disableTexCoord( ii);
		  }
      }
      else 
	  {
        //there is no binormal available
        glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
        m_glState.disableTexCoord( ii);
      }
      continue;
    }
    else if (m_mayaType[ii] == kColorSet) {

      //this set has requested a color
      if ( (m_setNums[ii] < colorCount) && colorArrays[m_setNums[ii]]) {
        //there is a color
        m_glState.enableAndActivateTexCoord( ii);
        glTexCoordPointer( 4, GL_FLOAT, 0, colorArrays[m_setNums[ii]]);
      }
      else {
        //there is no binormal available
        glMultiTexCoord4f( GL_TEXTURE0 + ii, 0.0f, 0.0f, 0.0f, 1.0f);
        m_glState.disableTexCoord( ii);
      }
      continue;
    }

    //now deal with regular sets
    if ( (m_setNums[ii] < texCoordCount) && texCoordArrays[m_setNums[ii]]) {
      //this set exists
      m_glState.enableAndActivateTexCoord( ii);
	  glTexCoordPointer( 2, GL_FLOAT, 0, texCoordArrays[m_setNums[ii]]);
    }
    else {
      //the set does not exist
      glMultiTexCoord2f( GL_TEXTURE0 + ii, 0.0f, 0.0f);
      m_glState.disableTexCoord( ii);
    }
  }

  //bind in the naked color sets

  //primary color first
    glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
    glDisableClientState( GL_COLOR_ARRAY);

  //handle the special numbers
  if (m_colorSetNums[0] == -1) {
    // no name is specified, send a default color of (1,1,1,1)
    glColor4f( 1.0f, 1.0f, 1.0f, 1.0f);
    glDisableClientState( GL_COLOR_ARRAY);
  }
  else if (m_colorMayaType[0] == kNormal)  {
	  
	  //this set has requested a normal
    if ( (normalCount > 0) && normalArrays[0]) {
      //there is a tangent
      glColorPointer( 3, GL_FLOAT, 0, normalArrays[0]);
      glEnableClientState( GL_COLOR_ARRAY);
    }
    else {
      //there is no normal available
      glColor4f( 0.0f, 0.0f, 1.0f, 1.0f);
      glDisableClientState( GL_COLOR_ARRAY);
    }
  }
  else if (m_colorMayaType[0] == kTangent)  {

	  //this set has requested a tangent
	  if ( m_numNormals > 1 && normalCount > 1)
	  {
		  int offset = 0;
		  if (m_numNormals > 2)
			  offset = ( m_colorSetNums[0] * 3 ) + 1;
		  else
			  offset = ( m_colorSetNums[0] * 2 ) + 1;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 2)
				  offset = 1;
			  else
				  offset = -1;
		  }

		  //printf("COLOR0: Access tangents via collection id(%d) = offset %d\n",
			//  m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset]) {
			  //there is a tangent
			  glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
			  glEnableClientState( GL_COLOR_ARRAY);
		  }
		  else {
			  //there is no tangent available
			  glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
			  glDisableClientState( GL_COLOR_ARRAY);
		  }
	  }
	  else {
		  //there is no tangent available
		  glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
		  glDisableClientState( GL_COLOR_ARRAY);
	  }	  	  
  }
  else if (m_colorMayaType[0] == kBinormal)  {

      //this set has requested a binormal
	  if ( m_numNormals > 2 && normalCount > 1)
	  {
		  int offset = ( m_colorSetNums[0] * 3 ) + 2;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 3)
				  offset = 2;
			  else
				  offset = -1;
		  }

		  //printf("COLOR0: Access binormals via collection id(%d) = offset %d\n",
			//  m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset] )
		  {
			  //there is a binormal
			  glColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
			  glEnableClientState( GL_COLOR_ARRAY);
		  }
		  else
		  {
			  //there is no binormal available
			  glMultiTexCoord3f( GL_TEXTURE0 + ii, 0.0f, 1.0f, 0.0f);
			  m_glState.disableTexCoord( ii);
		  }
	  }
	  else 
	  {
		  //there is no binormal available
		  glColor4f( 0.0f, 1.0f, 0.0f, 1.0f);
		  glDisableClientState( GL_COLOR_ARRAY);
	  }
  }
  // Route color set through color 0
  else if (m_colorMayaType[0] == kColorSet) {
    //this set has requested a color
    if ( (m_colorSetNums[0] < colorCount) && colorArrays[m_colorSetNums[0]]) {
      //there is a color
      glColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[0]]);
      glEnableClientState( GL_COLOR_ARRAY);
    }
    else {
      //there is no color available
      glColor4f( 0.0f, 0.0f, 0.0f, 1.0f);
      glDisableClientState( GL_COLOR_ARRAY);
    }
  }
  // Route a uv set through color 0
  else if (m_colorMayaType[0] == kUvSet) {
    //this set has requested a uvset
    if ( (m_colorSetNums[0] < texCoordCount) && texCoordArrays[m_colorSetNums[0]]) {
      // For now Maya only supports 2 compont tex coords.
      glColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[0]]);
      glEnableClientState( GL_COLOR_ARRAY);
    }
    else {
      //there is no color available
      glColor4f( 1.0f, 0.0f, 0.0f, 1.0f);
      glDisableClientState( GL_COLOR_ARRAY);
    }
  }


  //secondary color
  if (glSecondaryColorSupport) {

	glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
	glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
	  
	//handle the special numbers
    if (m_colorSetNums[1] == -1) {
		// no name is specified, send a default color of (1,1,1)
		glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
		glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
    }
    else if (m_colorMayaType[1] == kNormal)  {
      //this set has requested a normal
      if ( (normalCount > 0) && normalArrays[0]) {
        //there is a tangent
        glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[0]);
        glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
      }
      else {
        //there is no normal available
        glSecondaryColor3f( 0.0f, 0.0f, 1.0f);
        glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
      }
    }
    else if (m_colorMayaType[1] == kTangent)  {

	  //this set has requested a tangent
	  if ( m_numNormals > 1 && normalCount > 1)
	  {
		  int offset = 0;
		  if (m_numNormals > 2)
			  offset = ( m_colorSetNums[1] * 3 ) + 1;
		  else
			  offset = ( m_colorSetNums[1] * 2 ) + 1;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 2)
				  offset = 1;
			  else
				  offset = -1;
		  }

		  //printf("COLOR1: Access tangents via collection id(%d) = offset %d\n",
			//  m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset]) {
			  //there is a tangent
			  glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
			  glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
		  }
		  else {
			  //there is no tangent available
			  glSecondaryColor3f( 1.0f, 0.0f, 0.0f);
			  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
		  }
	  }
	  else {
		  //there is no tangent available
		  glSecondaryColor3f( 1.0f, 0.0f, 0.0f);
		  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
	  }		
    }
    else if (m_colorMayaType[1] == kBinormal)  {

      //this set has requested a binormal
	  if ( m_numNormals > 2 && normalCount > 1 )
	  {
		  int offset = ( m_colorSetNums[1] * 3 ) + 2;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 3)
				  offset = 2;
			  else
				  offset = -1;
		  }

		  //printf("COLOR1 : Access binormals via collection id(%d) = offset %d\n",
			//  m_setNums[ii], offset );

		  if ((offset > 0) && normalArrays[offset] )
		  {
			  //there is a binormal
			  glSecondaryColorPointer( 3, GL_FLOAT, 0, normalArrays[offset]);
			  glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
		  }
		  else
		  {
			  //there is no binormal available
			  glSecondaryColor3f( 0.0f, 1.0f, 0.0f);
			  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
		  }
	  }
	  else 
	  {
		  //there is no binormal available
		  glSecondaryColor3f( 0.0f, 1.0f, 0.0f);
		  glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
	  }		
    }
	// Route a color set through color 1
    else if (m_colorMayaType[1] == kColorSet) {
      //this set has requested a color
      if ( (m_colorSetNums[1] < colorCount) && colorArrays[m_colorSetNums[1]]) {
        //there is a color
        glSecondaryColorPointer( 4, GL_FLOAT, 0, colorArrays[m_colorSetNums[1]]);
        glEnableClientState( GL_SECONDARY_COLOR_ARRAY);
      }
      else {
        //there is no color available
        glSecondaryColor3f( 1.0f, 1.0f, 1.0f);
        glDisableClientState( GL_SECONDARY_COLOR_ARRAY);
      }
    }
#if 0
	// Route a uv set through color 1
	else if (m_colorMayaType[1] == kUvSet) {
		//this set has requested a uvset
		if ( (m_colorSetNums[1] < texCoordCount) && texCoordArrays[m_colorSetNums[1]]) {
			// For now Maya only supports 2 compont tex coords.
			glSecondaryColorPointer( 2, GL_FLOAT, 0, texCoordArrays[m_colorSetNums[1]]);
			glEnableClientState( GL_COLOR_ARRAY);
		}
		else {
			//there is no color available
			glColor4f( 0.0f, 0.0f, 0.0f, 1.0f);
			glDisableClientState( GL_COLOR_ARRAY);
		}
	}
#endif
  }


  //handle the generic attribute sets
  for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
    //I need to find a better place for this then here
    it->handle = m_shader->attributeHandle(it->pNum);
    if (it->set == -1) {
      glDisableVertexAttribArray( it->handle);
      glVertexAttrib2f( it->handle, 0.0f, 0.0f);
      continue;
    }
    else if (it->mtype == kNormal) {
      //this set has requested a tangent
      if (( normalCount > 0) && normalArrays[0]) {
        //there is a tangent
        glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[0]);
        glEnableVertexAttribArray( it->handle);
      }
      else {
        //there is no tangent available
        glVertexAttrib2f( it->handle, 0.0f, 0.0f);
        glDisableVertexAttribArray( it->handle);
      }
      continue;
    }
    else if (it->mtype == kTangent) {

	  //this set has requested a tangent
	  if ( m_numNormals > 1 && normalCount > 1)
	  {
		  int offset = 0;
		  if (m_numNormals > 2)
			  offset = ( it->set * 3 ) + 1;
		  else
			  offset = ( it->set * 2 ) + 1;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 2)
				  offset = 1;
			  else
				  offset = -1;
		  }

		  //printf("VTX_ATTRIB: Access tangents via collection id(%d) = offset %d\n",
			//  it->set, offset );

		  if ((offset > 0) && normalArrays[offset]) {
			  //there is a tangent
			  glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]);
			  glEnableVertexAttribArray( it->handle);
		  }
		  else {
			  //there is no tangent available
			  glVertexAttrib2f( it->handle, 0.0f, 0.0f);
			  glDisableVertexAttribArray( it->handle);
		  }
	  }
	  else {
		  //there is no tangent available
		  glVertexAttrib2f( it->handle, 0.0f, 0.0f);
		  glDisableVertexAttribArray( it->handle);
	  }				
      continue;
    }
    else if (it->mtype == kBinormal) {

      //this set has requested a binormal
	  if ( m_numNormals > 2 && normalCount > 1)
	  {
		  int offset = ( it->set * 3 ) + 2;

		  // Can occur with swatch rendering.
		  if (offset > normalCount)
		  {
			  if (normalCount >= 3)
				  offset = 2;
			  else
				  offset = -1;
		  }

		  //printf("VTX_ATTRIB : Access binormals via collection id(%d) = offset %d\n",
			//  it->set, offset );

		  if ((offset > 0) && normalArrays[offset] )
		  {
			  //there is a binormal
			  glVertexAttribPointer( it->handle, 3, GL_FLOAT, GL_FALSE, 0, normalArrays[offset]);
			  glEnableVertexAttribArray( it->handle);
		  }
		  else
		  {
			  //there is no binormal available
			  glVertexAttrib2f( it->handle, 0.0f, 0.0f);
			  glDisableVertexAttribArray( it->handle);
		  }
	  }
	  else
	  {
        //there is no binormal available
        glVertexAttrib2f( it->handle, 0.0f, 0.0f);
        glDisableVertexAttribArray( it->handle);
	  }
      continue;
    }
    else if (it->mtype == kColorSet) {
      //this set has requested a color
      if (( it->set < colorCount ) && colorArrays[it->set]) {
        //there is a color
        glVertexAttribPointer( it->handle, 4, GL_FLOAT, GL_FALSE, 0, colorArrays[it->set]);
        glEnableClientState( it->handle );
      }
      else {
        //there is no color available
        glVertexAttrib4f( it->handle, 0.0f, 0.0f, 0.0f, 1.0f);
        glDisableVertexAttribArray( it->handle);
      }
      continue;
    }


    //now deal with regular sets
    if (( it->set < texCoordCount ) && texCoordArrays[it->set]) {
      //this set exists
      glVertexAttribPointer( it->handle, 2, GL_FLOAT, GL_FALSE, 0, texCoordArrays[it->set]);
      glEnableVertexAttribArray( it->handle);
    }
    else {
      //the set does not exist
      glVertexAttrib2f( it->handle, 0.0f, 0.0f);
      glDisableVertexAttribArray( it->handle);
    }
  }

  m_glState.activeTexture( 0);
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
MStatus	glslShaderNode::glGeometry( const MDagPath& shapePath, int prim, unsigned int writable, int indexCount,
		const unsigned int * indexArray, int vertexCount, const int * vertexIDs, const float * vertexArray,
		int normalCount, const float ** normalArrays, int colorCount, const float ** colorArrays, int texCoordCount,
        const float ** texCoordArrays) {

  if (!(glShadingLanguageSupport && glMultiTextureSupport && glTexture3DSupport && glAsmProgramSupport))
    return MStatus::kFailure;

  // If we're not using the default shader, setup any shape specific 
  // shader parameters
  if( m_passCount > 0)
    updateBoundAttributes(shapePath);

  // this is inefficient for the moment
  int passCount = m_passCount >= 0 ? m_passCount : 1;
  for( int jj=0; jj < passCount; jj++) 
  {
	// If this effect has more than 1 pass, bind the
	// shader per-pass. If it's a single pass effect,
	// the pass will have been bound in glBind()
    if( passCount > 1) 
	{
		m_shader->setPass( jj);
		m_shader->bind();
	}

	// Setup any shape dependent uniforms (e.g. matrices)
	if( m_passCount > 0)
	{
		m_shader->setShapeDependentState();
    }
	else
	{
		sDefShader->setShapeDependentState();
	}

    //all of the following should come from shader attribute querying
    // might want to add VBO support for multiple pass shaders
    glVertexPointer( 3, GL_FLOAT, 0, vertexArray);
    
    if (normalCount && normalArrays[0] ) {
      glNormalPointer( GL_FLOAT, 0, &normalArrays[0][0]);
      glEnableClientState(GL_NORMAL_ARRAY);
    }
	else
	{
      glDisableClientState(GL_NORMAL_ARRAY);
	}
  
    configureTexCoords( normalCount, normalArrays, texCoordCount, texCoordArrays,
					    colorCount, colorArrays );
    

    GL_CHECK;

    //should probably switch to draw range elements
    glDrawElements( prim, indexCount, GL_UNSIGNED_INT, indexArray);

	// If this effect has more than 1 pass, unbind the
	// shader per-pass (if it's a single pass effect,
	// the effect will be bound once in glBind()
    if( m_passCount > 1) 
	{
      m_shader->unbind();
	}

    GL_CHECK;
  }// for num passes
	
  if( m_passCount > 1) 
	m_shader->setPass(0);

  return MS::kSuccess;
}

//
// If the the attribute is a shader attribute
// mark it dirty, and pass it to the parent
////////////////////////////////////////////////////////////////////////////////
MStatus			
glslShaderNode::connectionMade( const MPlug& plug,
							   const MPlug& otherPlug,
							   bool asSrc )
{

	//need to grab the root in case we are dealing with a vector 
	const MPlug &root = plug.isChild() ? plug.parent() : plug; 

	//MGlobal::displayInfo(MString("Connection made: ") + root.name());

	//
	//  Is this time critical enough to use something like a map, instead or linearly searching all
	// attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
	{
		for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it != m_uniformAttribList.end(); it++) {
			if ( root == it->attrib) {
				it->dirty = true;
				//allow the set to proceed using the default method
				break;
			}
		}
	}

	{
		for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it != m_samplerAttribList.end(); it++) {
			if ( root == it->attrib) {
				it->dirty = true;
				//allow the set to proceed using the default method
				break;
			}
		}
	}

	// Pass to parent class. Must do this to ensure keyframe
	// animation works !
	return MS::kUnknownParameter;
}

//
// If the the attribute is a shader attribute
// mark it dirty, and pass it to the parent
////////////////////////////////////////////////////////////////////////////////
MStatus		
glslShaderNode::connectionBroken( const MPlug& plug,
								 const MPlug& otherPlug,
								 bool asSrc )
{
	//need to grab the root in case we are dealing with a vector 
	const MPlug &root = plug.isChild() ? plug.parent() : plug; 

	//MGlobal::displayInfo(MString("Connection broken: ") + root.name());

	//
	//  Is this time critical enough to use something like a map, instead or linearly searching all
	// attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
	{
		for ( std::vector<uniformAttrib>::iterator it = m_uniformAttribList.begin(); it < m_uniformAttribList.end(); it++) {
			if ( root == it->attrib) {
				it->dirty = true;
				//allow the set to proceed using the default method
				break;
			}
		}
	}

	{
		for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {
			if ( root == it->attrib) {
				it->dirty = true;
				//allow the set to proceed using the default method
				break;
			}
		}
	}

	// Pass to parent class. Must do this to ensure keyframe
	// animation works !
	return MS::kUnknownParameter;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
/* virtual */
bool glslShaderNode::getInternalValueInContext( const MPlug &plug, 
											   MDataHandle &handle,
											   MDGContext& )
{
  bool retVal = false;

  if ( plug == sShader) {
    handle.set(m_shaderName);
    retVal = true;
  }
  else if ( plug == sTechnique) {
    handle.set(m_techniqueName);
    retVal = true;
  }
  else if ( plug == sTechniqueList )
  {
	handle.set(m_techniqueList);
	retVal = true;
  }
  else if ( plug == sShaderPath )
  {
	handle.set(m_shaderPath);
	retVal = true;
  }  
  else {
    int ii;
    for (ii=0; ii<8; ii++) {
      if ( plug == sNakedTexCoords[ii]) {
        handle.set(m_nakedTexSets[ii]);
        retVal = true;
        break;
      }
    }

    for (ii=0; ii<2; ii++) {
      if (plug == sNakedColors[ii]) {
        handle.set(m_nakedColorSets[ii]);
        retVal = true;
        break;
      }
    }

    //handle the generic attribute sets
    for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
      if (plug == it->attrib) {
        handle.set(it->setName);
        retVal = true;
        break;
      }
    }
  }

  return retVal;
}



//
//
//
////////////////////////////////////////////////////////////////////////////////
/* virtual */
bool glslShaderNode::setInternalValueInContext( const MPlug &plug, 
											   const MDataHandle &handle,
											   MDGContext & )
{
  bool retVal = false;
  
  //if reading file, short circuit things, we'll clean up post load
  bool reading = MFileIO::isReadingFile();
  if (reading)
	  m_shader = NULL;

  //MGlobal::displayInfo(MString("Plug update: ") + plug.name());

  if ( plug == sShader) {
    m_shaderName = handle.asString();
	m_shaderPath = m_shaderName;
    if (locateFile( m_shaderName, m_shaderPath)) {
	  // Set shader name to be full path so that we 
	  // can access a fully qualified name from the UI (e.g.
	  // for reloading, viewing and editing the fx file.
	  //
      m_shaderDirty = true;
      if (!reading) {

		  if (MGlobal::mayaState() == MGlobal::kBatch)
  			rebuildShader();
  		else
  		{
  			MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
  			if (pRenderer)
  			{
  				const MString & backEndStr = pRenderer->backEndString();
  				MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
  				if (status != MStatus::kSuccess) {
					MStatus strStat;
  					MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
  					MGlobal::displayError(status.errorString());
  				}
  				else
  					rebuildShader();
  			}
  		}
      }
    }
    else {
      MGlobal::displayError(MString("Couldn't find shader file: '") + m_shaderPath + "'");
    }
    retVal = true;
  }
  else if ( plug == sTechnique) {
    MString temp = handle.asString();
	// We are in file read and m_shader has not been built yet,
	// so don't print out an error message here.
	//
	if (reading)
	{
		m_techniqueName = temp;
		retVal = true;
	}
	else
	{
    if (m_shader) {
      for (int ii=0; ii<m_shader->techniqueCount(); ii++) {
        if (temp == m_shader->techniqueName(ii)) {
          m_shader->setTechnique(ii);
          retVal = true;
          break;
        }
      }
    }

    if (retVal)
      m_techniqueName = temp;
    else
		{
			MGlobal::displayWarning(MString("Unknown technique: ") + temp +
			MString(" for shader: ") + m_shaderPath.asChar());
		}
	}
    retVal = true;
  }
  else if ( plug == sTechniqueList )
  {
	  // Do nothing
  }
  else {

    int ii;

    //handle the naked texture coord sets
    for (ii=0; ii<8; ii++) {
      if (plug == sNakedTexCoords[ii]) {
        m_nakedTexSets[ii] = handle.asString();
        //MGlobal::displayInfo( MString("Setting Tex ") + ii + " to " + m_nakedTexSets[ii]);
        return true;
      }
    }

    for (ii=0; ii<2; ii++) {
      if (plug == sNakedColors[ii]) {
        m_nakedColorSets[ii] = handle.asString();
        //MGlobal::displayInfo( MString("Setting Color ") + ii + " to " + m_nakedColorSets[ii]);
        return true;
      }
    }

    //handle the generic attribute sets
    for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
      if (plug == it->attrib) {
        it->setName = handle.asString();
        return true;
      }
    }
    
    if (!reading) {
      //process dynamic attributes

      //need to grab the root in case we are dealing with a vector 
      const MPlug &root = plug.isChild() ? plug.parent() : plug; 
      
      //MGlobal::displayInfo(MString("Root name: ") + root.name());
  
      //
      //  Is this time critical enough to use something like a map, instead or linearly searching all
      // attribute? Present expectation is for shaders to have significantly fewer than 100 attributes.
      for ( std::vector<uniformAttrib>::iterator it2 = m_uniformAttribList.begin(); it2 < m_uniformAttribList.end(); it2++) {
        if ( ( root == it2->attrib) || ( root == it2->attrib2) ){
			it2->dirty = true;
          //allow the set to proceed using the default method
          break;
        }
      }

      for ( std::vector<samplerAttrib>::iterator it = m_samplerAttribList.begin(); it < m_samplerAttribList.end(); it++) {
        if (root == it->attrib) {
          it->dirty = true;
          //allow the set to proceed using the default method
          break;
        }
        if (root == it->anisoAttrib) {
          it->dirty = true;
          it->maxAniso = handle.asFloat();

          //allow the set to proceed using the default method
          break;
        }
        if (root == it->magFilterAttrib) {
          it->dirty = true;
          switch (handle.asShort()) {
            case 0:
              it->magFilter = GL_NEAREST;
              break;
            case 1:
              it->magFilter = GL_LINEAR;
              break;
            default:
              //this should be impossible
              it->magFilter = GL_LINEAR;
              break;
          };

          //allow the set to proceed using the default method
          break;
        }
        if (root == it->minFilterAttrib) {
          it->dirty = true;
          switch (handle.asShort()) {
            case 0:
              it->minFilter = GL_NEAREST;
              break;
            case 1:
              it->minFilter = GL_LINEAR;
              break;
            case 2:
              it->minFilter = GL_LINEAR_MIPMAP_NEAREST;
              break;
            case 3:
              it->minFilter = GL_LINEAR_MIPMAP_LINEAR;
              break;
            default:
              //this should be impossible
              it->minFilter = GL_LINEAR;
              break;
          };

          //allow the set to proceed using the default method
          break;
        }
        if (root == it->wrapAttrib) {
          it->dirty = true;
          switch (handle.asShort()) {
            case 0:
              it->wrapMode = GL_REPEAT;
              break;
            case 1:
              it->wrapMode = GL_CLAMP_TO_EDGE;
              break;
            case 2:
              it->wrapMode = GL_MIRRORED_REPEAT;
              break;
            default:
              //this should be impossible
              it->wrapMode = GL_REPEAT;
              break;
          };

          //allow the set to proceed using the default method
          break;
        }
      }
    }
  }

  GL_CHECK;

  return retVal;
}

void glslShaderNode::copyInternalData( MPxNode* pMPx )
{
	glslShaderNode* pNode = dynamic_cast<glslShaderNode*>( pMPx );

	m_shader = NULL;
	m_shaderDirty = false;

	// These get rebuilt
	m_uniformAttribList.clear();
	m_samplerAttribList.clear();
	m_attributeAttribList.clear();
	m_boundUniformList.clear();

	// Reset temporaries
	m_parsedUserAttribList = false;
	m_numUVsets = 0;
	m_uvSetNames.clear();
	m_numColorSets = 0;
	m_colorSetNames.clear();	
	m_numNormals = 3;
	m_maxSetNum = 0;

	if( pNode )
	{
		m_maxSetNum = pNode->m_maxSetNum;

		for(int ii=0; ii<8; ii++)
		{
			m_nakedTexSets[ii] = pNode->m_nakedTexSets[ii];
			m_setNums[ii] = pNode->m_setNums[ii];
			m_mayaType[ii] = pNode->m_mayaType[ii];
		}

		m_nakedColorSets[0] = pNode->m_nakedColorSets[0];
		m_nakedColorSets[1] = pNode->m_nakedColorSets[1];

		m_colorSetNums[0] = pNode->m_colorSetNums[0]; 
		m_colorSetNums[1] = pNode->m_colorSetNums[1]; 

		m_colorMayaType[0] = pNode->m_colorMayaType[0];
		m_colorMayaType[1] = pNode->m_colorMayaType[1];

		m_shaderName = pNode->m_shaderName;
		m_shaderPath = m_shaderName;		
		m_techniqueList = "";
		m_techniqueName = "";

		if (locateFile( m_shaderName, m_shaderPath))
		{
			// Must be done before rebuild to set the correct technique
			m_techniqueName = pNode->m_techniqueName;
			m_shaderDirty = true;

			MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
			if (pRenderer)
			{
				const MString & backEndStr = pRenderer->backEndString();
				MStatus status = pRenderer->makeResourceContextCurrent( backEndStr );
				if (status != MStatus::kSuccess) {
					MStatus strStat;
					MGlobal::displayError(MStringResource::getString(rGLSLShaderNodeFailedResourceContext, strStat));
					MGlobal::displayError(status.errorString());
				}
				else
					rebuildShader();
			}
			else
				rebuildShader();
		}
	}
}

//
//attribute request interface
//
////////////////////////////////////////////////////////////////////////////////
int glslShaderNode::colorsPerVertex() {
  return 1;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
int glslShaderNode::texCoordsPerVertex() {
    return 0;
}

#if MAYA_API_VERSION >= 700
  //  There are two versions of parseUserAttributeList, so the plug-in
  // can work on multiple revisions of Maya. The version for 700 and
  // above relies on the presence of the named color sets, and a fix
  // to a bug in the Maya API. 

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::parseUserAttributeList()
{
  if (m_parsedUserAttribList)
    return;

  // Reset shared data which is used by getColorSetNames() and
  // getTexCoordSetNames
  //
  m_numUVsets = 0;
  m_numColorSets = 0;
  m_uvSetNames.clear();
  m_colorSetNames.clear();


  m_numNormals = 3;

  MStringArray uvSetNames;
  MStringArray colorSetNames;

  bool haveNurbs = false;
  // "Magic" name for nurbs uvs
  MString nurbsDefaultUV("Nurbs Tri UVs"); 
  const MDagPath & path = currentPath();	
  if (path.hasFn( MFn::kMesh ) )
  {

    // Check the # of uvsets
    MFnMesh fnMesh( path.node() );
    if (fnMesh.numUVSets())
      fnMesh.getUVSetNames(uvSetNames);

    // Check the # of color sets
    if (fnMesh.numColorSets())
      fnMesh.getColorSetNames(colorSetNames);
  }
  else // is a NURBS
  {
    haveNurbs = true;
    uvSetNames.append(nurbsDefaultUV);

    // There is no such thing as color sets no nurbs
    // for now, so don't add any "default"
  }

  //
  // The code here needs to work as follows:
  //  1. Add nurbs uv set if it is a nurbs.
  //  2. Iterate the list of requested maps
  //  3. Check for special maps (tangent/binormal)
  //  4. If the map is not yet added, add it
  //  5. Record the index

  int ii = 0;
  for (; ii<8; ii++) 
  {
    m_setNums[ii] = -1;
    m_mayaType[ii] = kNonMaya;
  }


  // Fixed splitting character to find qualifiers for inputs.
  // For now only used to scope the tangent, and binormal to
  // a given tangent or binormal set.
  //
  const char _splitChar = ':';

  ii = 0;
  for (; ii<8; ii++) 
  {
	  MStringArray splitStrings;	
	  MString comparisonString = m_nakedTexSets[ii];
	  if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
		   splitStrings.length() > 1)
	  {
		  comparisonString = splitStrings[0];
	  }

	  // Special case nurbs. If "map1" is specified then
	  // append the "special" uv set name for nurbs instead
	  if (haveNurbs && comparisonString == "map1") 
	  {
		  m_setNums[ii] = m_uvSetNames.length();
		  m_uvSetNames.append(nurbsDefaultUV);
		  m_mayaType[ii] = kUvSet;
		  continue;
	  }
	  else if (comparisonString == "normal") {
		  m_setNums[ii] = 0; // Normal is currently always for set 0
		  m_mayaType[ii] = kNormal;
		  continue;
	  }
	  else if (comparisonString == "tangent") {
		  m_setNums[ii] = 0; // Tangent is currently always for set 0
		  m_mayaType[ii] = kTangent;
		  continue;
	  }
	  // Note that for NURBS there is currently an API bug
	  // in that you cannot get the binormals even when you
	  // ask for them.
	  else if (comparisonString == "binormal") {
		  m_setNums[ii] = 0; // Binormal is currently always for set 0
		  m_mayaType[ii] = kBinormal;
		  continue;
	  }
	  else if (comparisonString == "") {
		  m_setNums[ii] = -1;
		  m_mayaType[ii] = kNonMaya;
		  continue;
	  }
	  else {
		  // Test out uv set name matches first
		  bool matchUVSet = false;
		  unsigned int numNames = uvSetNames.length();
		  if (numNames)
		  {
			  unsigned int jj = 0;
			  for (jj=0; jj<numNames; jj++) {
				  if (comparisonString == uvSetNames[jj])
				  {
					  // Track name, number and type
					  m_setNums[ii] = m_uvSetNames.length();
					  m_uvSetNames.append( uvSetNames[jj] );
					  m_mayaType[ii] = kUvSet;
					  matchUVSet = true;
					  break;
				  }
			  }
		  }

		  // Test out color set name matches second
		  if (!matchUVSet)
		  {
			  numNames = colorSetNames.length();
			  if (numNames)
			  {
				  unsigned int jj = 0;
				  for (jj=0; jj<numNames ; jj++) {
					  if (comparisonString == colorSetNames[jj])
					  {
						  // Track name, number and type
						  m_setNums[ii] = m_colorSetNames.length();
						  m_colorSetNames.append( colorSetNames[jj] );
						  m_mayaType[ii] = kColorSet;
						  break;
					  }
				  }
			  }
		  }
	  }
  }


  //now handle the colors
  //
  // Colors need to be either 3 or 4 components, so they do not
  // draw from the texture coord sets.
  ii = 0;
  for (; ii<2; ii++) 
  {
	  m_colorSetNums[ii] = -1;
	  m_colorMayaType[ii] = kNonMaya;
  }
  ii = 0;
  for (; ii<2; ii++) 
  {
	  MStringArray splitStrings;	
	  MString comparisonString = m_nakedColorSets[ii];
	  if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
		  splitStrings.length() > 1)
	  {
		  comparisonString = splitStrings[0];
	  }


	  if (comparisonString == "normal") {
		  m_colorSetNums[ii] = 0; // Normal is currently always for set 0
		  m_colorMayaType[ii] = kNormal;
		  continue;
	  }
	  else if (comparisonString == "tangent") {
		  m_colorSetNums[ii] = 0; // Tangent is currently always for set 0
		  m_colorMayaType[ii] = kTangent;
		  continue;
	  }
	  // Note that for NURBS there is currently an API bug
	  // in that you cannot get the binormals even when you
	  // ask for them.
	  else if (comparisonString == "binormal") {
		  m_colorSetNums[ii] = 0; // Binormal is currently always for set 0
		  m_colorMayaType[ii] = kBinormal;
		  continue;
	  }
	  else if (comparisonString == "") {
		  m_colorSetNums[ii] = -1;
		  m_colorMayaType[ii] = kNonMaya;
		  continue;
	  }
	  else {

		  // Test out color set name matches first.
		  // Basically the opposite of uv set lookup.
		  //
		  bool matchUVSet = false;

		  // Test out color set name matches
		  unsigned int numNames = colorSetNames.length();
		  if (numNames)
		  {
			  unsigned int jj = 0;
			  for (jj=0; jj<numNames ; jj++) {
				  if (comparisonString == colorSetNames[jj])
				  {
					  // Track name, number and type
					  m_colorSetNums[ii] = m_colorSetNames.length();
					  m_colorSetNames.append( colorSetNames[jj] );
					  m_colorMayaType[ii] = kColorSet;
					  matchUVSet = true;
					  break;
				  }
			  }
		  }

#ifdef _INTERACTIVE_SHADING_WORKS_FOR_ROUTING_TEXCOORDS_TO_COLOUR
		  // Currently this crashes when not in hi-quality mode
		  // so disable.
		  numNames = uvSetNames.length();
		  if (numNames)
		  {
			  unsigned int jj = 0;
			  for (jj=0; jj<numNames; jj++) {
				  if (comparisonString == uvSetNames[jj])
				  {
					  // Track name, number and type
					  m_colorSetNums[ii] = m_uvSetNames.length();
					  m_uvSetNames.append( uvSetNames[jj] );
					  m_colorMayaType[ii] = kUvSet;
					  break;
				  }
			  }
		  }
#endif

	  }
  }

  // now handle the generic attributes
  // unsigned int setNum = 0;
  for (std::vector<attributeAttrib>::iterator it = m_attributeAttribList.begin(); 
	  it<m_attributeAttribList.end(); it++) 
  {
	  MStringArray splitStrings;	
	  MString comparisonString( it->setName );
	  if ((MStatus::kSuccess == comparisonString.split(_splitChar, splitStrings)) &&
		   splitStrings.length() > 1)
	  {
		  comparisonString = splitStrings[0];
	  }

	  if (comparisonString == "normal") {
		  it->mtype = kNormal;
		  it->set = 0;
		  continue;
	  }
	  else if (comparisonString == "tangent") {
		  it->mtype = kTangent;
		  it->set = 0;
		  continue;
	  }
	  else if (comparisonString == "binormal") {
		  it->mtype = kBinormal;
		  it->set = 0;
		  continue;
	  }
	  else if (comparisonString == "") {
		  it->set = -1;
		  continue;
	  }
	  else {
		  // Test out uv set name matches first
		  bool matchUVSet = false;
		  unsigned int numNames = uvSetNames.length();
		  if (numNames)
		  {
			  unsigned int jj = 0;
			  for (jj=0; jj<numNames; jj++) 
			  {
				  // Track name, number and type
				  if (comparisonString == uvSetNames[jj]) 
				  {
					  it->set = m_uvSetNames.length();
					  m_uvSetNames.append( uvSetNames[jj] );
					  it->mtype = kUvSet;
					  matchUVSet = true;
					  break;
				  }
			  }
		  }

		  // Test out color set name matches second
		  if (!matchUVSet)
		  {
			  numNames = colorSetNames.length();
			  if (numNames)
			  {
				  unsigned int jj = 0;
				  for (jj=0; jj<numNames ; jj++) {
					  if (m_nakedTexSets[ii] == colorSetNames[jj])
					  {
						  // Track name, number and type
						  m_colorSetNames.append( colorSetNames[jj] );
						  it->set = m_colorSetNames.length();
						  it->mtype = kColorSet;
						  break;
					  }
				  }
			  }
		  }
	  }
  }

  /////////////////////////////////////////////////////////////////////////////////////////


  // Do a post-scan to see if a specific uvset is specified for tangent
  // and binormal in the uv set entries.
  //
  //printf("----------------------- POST SCAN TEXCOORDS --------------------\n");
  unsigned int numUVSets = uvSetNames.length();
  if (numUVSets)
  {
	  for (ii=0; ii<8; ii++) 
	  {

		  if (m_mayaType[ii] == kTangent || m_mayaType[ii] == kBinormal)
		  {
			  MStringArray splitStrings;
			  MString origString( m_nakedTexSets[ii] );

			  //printf("Attempt to split the TEXCOORD string[%d] = %s.\n",
				//		ii, origString.asChar());

			  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
				  splitStrings.length() > 1)
			  {
				  // Look for a match of existing uvset names

				  //printf("--- Managed to split the string[%d]. Look for uvset %s\n",
					//  ii, splitStrings[1].asChar());
				  unsigned int jj = 0;
				  for (jj=0; jj<numUVSets; jj++)
				  {
					  if (uvSetNames[jj] == splitStrings[1])
					  {
						  int foundSet = -1;
						  for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
						  {
							  if (splitStrings[1] == m_uvSetNames[kk])
							  {
								  foundSet = kk;
								  break;
							  }
						  }

						  if (foundSet > 0)
						  {
							  // Set number is index into uvset name array.
							  m_setNums[ii] = foundSet;
						  }
						  else
						  {
							  // Append a new uvset name, and point to end of
							  // uvset name array.
							  m_setNums[ii] = m_uvSetNames.length();
							  m_uvSetNames.append( splitStrings[1] );
						  }

						  //printf("---- Add uvset Index=%d. uvset name %s\n", m_setNums[ii],
							//	splitStrings[1].asChar());

						  break;
					  }
				  }
			  }
		  }
	  }
  }


  // Do a post-scan to see if a specific uvset is specified for tangent
  // and binormal in the color set entries.
  //
//  printf("----------------------- POST SCAN COLORS --------------------\n");
  if (numUVSets)
  {
	  for (ii=0; ii<2; ii++) 
	  {
		  if (m_colorMayaType[ii] == kTangent || m_colorMayaType[ii] == kBinormal)
		  {
			  MStringArray splitStrings;
			  MString origString( m_nakedColorSets[ii] );

			  //printf("Attempt to split the COLOR string[%d] = %s.\n",
				//		ii, origString.asChar());

			  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
				  splitStrings.length() > 1)
			  {
				  // Look for a match of existing uvset names
				  unsigned int jj = 0;
				  for (jj=0; jj<numUVSets; jj++)
				  {
					  if (uvSetNames[jj] == splitStrings[1])
					  {
						  int foundSet = -1;
						  for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
						  {
							  if (splitStrings[1] == m_uvSetNames[kk])
							  {
								  foundSet = kk;
								  break;
							  }
						  }

						  if (foundSet > 0)
						  {
							  // Set number is index into uvset name array.
							  m_colorSetNums[ii] = foundSet;
						  }
						  else
						  {
							  // Append a new uvset name, and point to end of
							  // uvset name array.
							  m_colorSetNums[ii] = m_uvSetNames.length();
							  m_uvSetNames.append( splitStrings[1] );
						  }

						  //printf("---- Add uvset Index=%d. uvset name %s\n", m_colorSetNums[ii],
							//	splitStrings[1].asChar());

						  break;
					  }
				  }
			  }
		  }
	  }
  }


  // Do a post-scan to see if a specific uvset is specified for tangent
  // and binormal in the generic entries.
  //
  if (numUVSets)
  {
	  for (std::vector<attributeAttrib>::iterator it2 = m_attributeAttribList.begin(); 
		  it2<m_attributeAttribList.end(); it2++)
	  {
		  if (it2->mtype == kTangent || it2->mtype == kBinormal)
		  {
			  MStringArray splitStrings;
			  MString origString( it2->setName );
			  if ((MStatus::kSuccess == origString.split(_splitChar, splitStrings)) &&
				  splitStrings.length() > 1)
			  {
				  // Look for a match of existing uvset names
				  unsigned int jj = 0;
				  for (jj=0; jj<numUVSets; jj++)
				  {
					  if (uvSetNames[jj] == splitStrings[1])
					  {
						  int foundSet = -1;
						  for (unsigned int kk=0; kk<m_uvSetNames.length(); kk++)
						  {
							  if (splitStrings[1] == m_uvSetNames[kk])
							  {
								  foundSet = kk;
								  break;
							  }
						  }

						  if (foundSet > 0)
						  {
							  // Set number is index into uvset name array.
							  it2->set = foundSet;
						  }
						  else
						  {
							  // Append a new uvset name, and point to end of
							  // uvset name array.
							  it2->set = m_uvSetNames.length();
							  m_uvSetNames.append( splitStrings[1] );
						  }

						  //printf("---- uvset Index=%d. uvset name %s\n", it->set,
							//	splitStrings[1].asChar());

						  break;
					  }		
				  }
			  }
		  }
	  }
  }

  m_numUVsets = m_uvSetNames.length();
  m_numColorSets = m_colorSetNames.length();

	m_maxSetNum = 0;
	for ( ii = 0; ii<8; ii++) 
		if( m_mayaType[ii] != kNonMaya)
			m_maxSetNum = ii + 1;

  m_parsedUserAttribList = true;
}

#else // if MAYA_7

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::parseUserAttributeList()
{
  if (m_parsedUserAttribList)
    return;

  // Reset shared data which is used by getColorSetNames() and
  // getTexCoordSetNames
  //
  m_numUVsets = 0;
  m_numColorSets = 0;
  m_uvSetNames.clear();
  m_colorSetNames.clear();

  //
  //  Always requesting a default set of map1 first, works around
  // an annyoing bug seen in Maya 6.5.
  //
  m_uvSetNames.append("map1");

  int ii = 0;
  for (; ii<8; ii++) 
  {
    m_setNums[ii] = -1;
    m_mayaType[ii] = kNonMaya;
  }
  ii = 0;
  for (; ii<8; ii++) 
  {
    // Special case, grab the special set from set 0
    if ( m_nakedTexSets[ii] == "map1") 
    {
      m_setNums[ii] = 0;
      m_mayaType[ii] = kUvSet;
      continue;
    }
    else if (m_nakedTexSets[ii] == "normal") {
      m_setNums[ii] = 0; // Normal is currently always for set 0
      m_mayaType[ii] = kNormal;
      continue;
    }
    else if (m_nakedTexSets[ii] == "tangent") {
      m_setNums[ii] = 0; // Tangent is currently always for set 0
      m_mayaType[ii] = kTangent;
      continue;
    }
    // Note that for NURBS there is currently an API bug
    // in that you cannot get the binormals even when you
    // ask for them.
    else if (m_nakedTexSets[ii] == "binormal") {
      m_setNums[ii] = 0; // Binormal is currently always for set 0
      m_mayaType[ii] = kBinormal;
      continue;
    }
    else if (m_nakedTexSets[ii] == "") {
      m_setNums[ii] = -1;
      m_mayaType[ii] = kNonMaya;
      continue;
    }
    else {
      //just append the set
      m_setNums[ii] = m_uvSetNames.length();
      m_uvSetNames.append( m_nakedTexSets[ii] );
      m_mayaType[ii] = kUvSet;
    }
  }

  //now handle the generic attributes
  unsigned int setNum = 0;
  for (std::vector<attributeAttrib>::iterator it = m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
    if (it->setName == "normal") {
      it->mtype = kNormal;
      it->set = 0;
      continue;
    }
    else if (it->setName == "tangent") {
      it->mtype = kTangent;
      it->set = 0;
      continue;
    }
    else if (it->setName == "binormal") {
      it->mtype = kBinormal;
      it->set = 0;
      continue;
    }
    else if (it->setName == "") {
      it->set = -1;
      continue;
    }
    else {
      //just append the set
      it->set = m_uvSetNames.length();
      m_uvSetNames.append( it->setName );
      it->mtype = kUvSet;
     
    }
  }

  m_numUVsets = m_uvSetNames.length();
  m_numColorSets = m_colorSetNames.length();
	m_maxSetNum = 0;
	for ( ii = 0; ii<8; ii++) 
		if( m_mayaType[ii] != kNonMaya)
			m_maxSetNum = ii + 1;

  m_parsedUserAttribList = true;
}

#endif // if MAYA_7

//
//
//
////////////////////////////////////////////////////////////////////////////////
int	glslShaderNode::getColorSetNames(MStringArray& names)
{
	if (!m_parsedUserAttribList)
		parseUserAttributeList();

	names = m_colorSetNames;
	return m_numColorSets;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
int glslShaderNode::getTexCoordSetNames( MStringArray &names) 
{	
	if (!m_parsedUserAttribList)
		parseUserAttributeList();

	names = m_uvSetNames;
	return m_numUVsets;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
int glslShaderNode::normalsPerVertex() {

  // this can be revisited, now that we have dynamic attributes
  m_numNormals = 3;
  return m_numNormals;
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::configureUniformAttrib( MString &name, int pNum, shader::DataType type, shader::Semantic sm, float *defVal,
                                            MFnDependencyNode &dn, MDGModifier &mod) {
  uniformAttrib ua;
  MFnNumericAttribute nAttrib;
  MFnTypedAttribute tAttrib;
  MStatus stat;

  //set the generic attribute values 
  ua.name = name;
  ua.pNum = pNum;
  ua.dirty = true;
  ua.type = type;
  ua.attrib = MObject::kNullObj;
  ua.attrib2 = MObject::kNullObj;

  // [ANIM_UNIFORM_FIX1] : 
  // Note that all uniforms are not set as being internal as this
  // makes animation playback unreliable. [See related code for binding
  // unforms [ANIM_UNIFORM_FIX2]]

  //obtain relavent information
  float min=0.0f, max=0.0f;
  bool color = shader::isColor(sm);
  bool clamped = shader::isClamped(sm);
  shader::getLimits( sm, min, max);

  switch (ua.type) {

    case shader::dtFloat:
      if ( !dn.hasAttribute( name )) {
        ua.attrib = nAttrib.create( name, name, MFnNumericData::kFloat);
        if (clamped) {
          nAttrib.setMin(min);
          nAttrib.setMax(max);
        }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( defVal[0]);
        else
          nAttrib.setDefault( -1.0 );
		stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtVec2:
      if ( !dn.hasAttribute( name )) {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Float);
        if (clamped) {
          nAttrib.setMin( min, min);
          nAttrib.setMax( max, max);
        }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( defVal[0], defVal[1] );
        else
          nAttrib.setDefault( -1.0, -1.0 );
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtVec3:
      if ( !dn.hasAttribute( name )) {
        if (color) {
          ua.attrib = nAttrib.createColor( name, name);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
          nAttrib.setInternal(true);
#endif
		  nAttrib.setKeyable( true );
          if (defVal)
            nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
        }
        else {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float);
          if (clamped) {
            nAttrib.setMin( min, min, min);
            nAttrib.setMax( max, max, max);
          }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
        else
          nAttrib.setDefault( -1.0, -1.0, -1.0 );
        }
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtVec4:
      if ( !dn.hasAttribute( name )) {
        if (color) {
          ua.attrib = nAttrib.createColor( name, name);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
          nAttrib.setInternal(true);
#endif
		  nAttrib.setKeyable( true );
          if (defVal)
            nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
          stat = mod.addAttribute( thisMObject(), ua.attrib );
          ua.attrib2 = nAttrib.create( name + "Alpha", name + "Alpha", MFnNumericData::kFloat);
          if (clamped) {
            nAttrib.setMin( 0.0f);
            nAttrib.setMax( 1.0f);
          }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
          nAttrib.setInternal(true);
#endif
		  nAttrib.setKeyable( true );
          if (defVal)
            nAttrib.setDefault( defVal[3]);
          else
            nAttrib.setDefault( 1.0 );
          stat = mod.addAttribute( thisMObject(), ua.attrib2 );
        }
        else {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Float);
          if (clamped) {
            nAttrib.setMin( min, min, min);
            nAttrib.setMax( max, max, max);
          }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( defVal[0], defVal[1], defVal[2] );
        else
          nAttrib.setDefault( -1.0, -1.0, -1.0 );
        stat = mod.addAttribute( thisMObject(), ua.attrib );
        ua.attrib2 = nAttrib.create( name + "W", name + "W", MFnNumericData::kFloat);
          if (clamped) {
            nAttrib.setMin( min);
            nAttrib.setMax( max);
          }
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( defVal[3]);
        else
          nAttrib.setDefault( -1.0 );
        stat = mod.addAttribute( thisMObject(), ua.attrib2 );
      }
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
        ua.attrib2 = dn.attribute( name + "W", &stat);
      }
      break;

    case shader::dtBool:
      if ( !dn.hasAttribute( name )) {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::kBoolean);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        nAttrib.setDefault( true );  //Will this work?
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtInt:
      if ( !dn.hasAttribute( name )) {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::kInt);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        if (defVal)
          nAttrib.setDefault( (int)defVal[0]);
        else
          nAttrib.setDefault( -1 );
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtIVec2:
      if ( !dn.hasAttribute( name )) {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::k2Int);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        nAttrib.setDefault( -1, -1 );
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtIVec3:
      if ( !dn.hasAttribute( name )) {
		ua.attrib = nAttrib.create( name, name, MFnNumericData::k3Int);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        nAttrib.setInternal(true);
#endif
        nAttrib.setKeyable( true );
        nAttrib.setDefault( -1, -1, -1 );
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtMat4:
      if ( !dn.hasAttribute( name )) {
        MFnMatrixAttribute mAttrib;
        ua.attrib = mAttrib.create( name, name, MFnMatrixAttribute::kFloat);
#ifdef _CAN_ANIMATE_INTERNAL_DYNAMIC_ATTRIBS
        mAttrib.setInternal(true);
#endif
		mAttrib.setKeyable( true );
        //How do I set default????
        stat = mod.addAttribute( thisMObject(), ua.attrib );
      }
      else {
        //just grab it, need to make sure it is correct
        ua.attrib = dn.attribute(name, &stat);
      }
      break;

    case shader::dtBVec2:
    case shader::dtBVec3:
    case shader::dtIVec4:
    case shader::dtBVec4:
    case shader::dtMat2:
    case shader::dtMat3:
      //not yet supported, primarly due to no native Maya type
      return;
    default:
      MGlobal::displayWarning(MString("Unknown uniform parameter type for: ") + name);
  };
  if (stat != MStatus::kSuccess) {
	MStatus strStat;
	MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
	error.format(error, name);
	MGlobal::displayError(error);
    MGlobal::displayError(stat.errorString());
  }
  m_uniformAttribList.push_back(ua); //will this cause reference counting problems?
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
void glslShaderNode::configureAttribs() {
  MDGModifier mod;

  {
	  for (std::vector<uniformAttrib>::iterator it=m_uniformAttribList.begin(); it<m_uniformAttribList.end(); it++) {
		  it->pNum = -1; //mark it as unused
	  }
  }

  {
	  for (std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin(); it<m_samplerAttribList.end(); it++) {
		  it->pNum = -1; //mark it as unused
	  }
  }

  {
	  for (std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin(); it<m_attributeAttribList.end(); it++) {
		  it->pNum = -1; //mark it as unused
	  }
  }

  //no need to worry about reattachment with these, they don't create attributes
  m_boundUniformList.clear();

  MFnDependencyNode dn(thisMObject());

  int ii;

  if (m_shader && m_shader->valid()) {

    //find the uniforms that the new shader uses
    for (ii=0; ii<m_shader->uniformCount(); ii++) {
      MString name(m_shader->uniformName(ii));

      //determine if it is bound or not
      if ( shader::isBound(m_shader->uniformSemantic(ii))) {

        //handle bound uniforms

        //add a new one to the list
        boundUniform bu;
        bu.name = name;
        bu.usage = m_shader->uniformSemantic(ii);
        bu.pNum = ii;

        m_boundUniformList.push_back(bu);
      }
      else {
        //handle uniform attributes (tweakables)
        //iterate the list to find it
        std::vector<uniformAttrib>::iterator it=m_uniformAttribList.begin();

        //might want to store this stuff in a map instead
        while (it < m_uniformAttribList.end()) {
          if (it->name == name)
            break; //found it
          it++; // look at the next
        }

        //see if we found the correct attribute
        if (it !=m_uniformAttribList.end()) {
          // make sure we have the same type
          if ( it->type == m_shader->uniformType(ii)) {

            //record the handle, and mark it as dirty, so we update it
            it->pNum = ii;
            it->dirty = true;

            //process the next attribute
            continue;
          }
          else {
            //need to delete the old one here, otherwise we can't create the new one
            mod.removeAttribute( thisMObject(), it->attrib);
            it->attrib = MObject::kNullObj;

            //check to see if it was a vec4, and kill the last component
            if (it->attrib2 != MObject::kNullObj) {
              mod.removeAttribute( thisMObject(), it->attrib2);
              it->attrib2 = MObject::kNullObj;
            }
          }
        }
        
        //if there was no appropriate attribute create a new one and add it
        configureUniformAttrib( name, ii, m_shader->uniformType(ii), m_shader->uniformSemantic(ii),
                                m_shader->uniformDefault(ii), dn, mod);
          
        GL_CHECK;
      }
    }

    //samplers
    for (ii=0; ii<m_shader->samplerCount(); ii++) {
      MString name(m_shader->samplerName(ii));

      //iterate the list to find it
      std::vector<samplerAttrib>::iterator it=m_samplerAttribList.begin();

      //might want to store this stuff in a map instead
      while (it < m_samplerAttribList.end()) {
        if (it->name == name)
          break; //found it
        
        it++; // look at the next
      }

      if (it !=m_samplerAttribList.end()) {
        //if found, set the pNum to point to this one
        it->pNum = ii;
        it->dirty = false;
        m_shader->updateSampler( ii, it->texName);
      }
      else {
        //if not found, create a new one and add it
        samplerAttrib sa;
        MFnNumericAttribute nAttrib;
        MFnEnumAttribute eAttrib;
        MStatus stat;

        sa.name = name;
        sa.pNum = ii;
        sa.dirty = true;
        sa.texName = 0;
        sa.minFilter = GL_LINEAR;
        sa.magFilter = GL_LINEAR;
        sa.wrapMode = GL_REPEAT;
        sa.maxAniso = 1.0f;

        if ( !dn.hasAttribute( name )) {
          sa.attrib = nAttrib.createColor( name, name);
          nAttrib.setInternal(true);
          stat = mod.addAttribute( thisMObject(), sa.attrib);
        }
        else {
          sa.attrib = dn.attribute(name, &stat);
        }
        if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
			error.format(error, name);
			MGlobal::displayError(error);
	        MGlobal::displayError(stat.errorString());
        }
        if ( !dn.hasAttribute( name +"MinFilter")) {
          sa.minFilterAttrib = eAttrib.create( name+"MinFilter", name+"MinFilter");
          eAttrib.addField( "Nearest", 0);
          eAttrib.addField( "Linear", 1);
          eAttrib.addField( "Linear, Mipmaped", 2);
          eAttrib.addField( "Trilinear", 3);
          eAttrib.setDefault( 1);
          eAttrib.setInternal(true);
          
          stat = mod.addAttribute( thisMObject(), sa.minFilterAttrib);
        }
        else {
          sa.minFilterAttrib = dn.attribute(name+"MinFilter", &stat);
          //need to query the filter attribute
		  MPlug plug(thisMObject(), sa.minFilterAttrib);
          short val;
          plug.getValue(val);
          switch (val) {
            case 0:
              sa.minFilter = GL_NEAREST;
              break;
            case 1:
              sa.minFilter = GL_LINEAR;
              break;
            case 2:
              sa.minFilter = GL_LINEAR_MIPMAP_NEAREST;
              break;
            case 3:
              sa.minFilter = GL_LINEAR_MIPMAP_LINEAR;
              break;
            default:
              //this should be impossible
              sa.minFilter = GL_LINEAR;
              break;
          };
        }
        if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
			error.format(error, name + "MinFilter");
			MGlobal::displayError(error);
			MGlobal::displayError(stat.errorString());
        }
        if ( !dn.hasAttribute( name +"MagFilter")) {
          sa.magFilterAttrib = eAttrib.create( name+"MagFilter", name+"MagFilter");
          eAttrib.addField( "Nearest", 0);
          eAttrib.addField( "Linear", 1);
          eAttrib.setDefault( 1);
          eAttrib.setInternal(true);
          
          stat = mod.addAttribute( thisMObject(), sa.magFilterAttrib);
        }
        else {
          sa.magFilterAttrib = dn.attribute(name+"MagFilter", &stat);
          //need to query the filter attribute
          MPlug plug(thisMObject(), sa.magFilterAttrib);
          short val;
          plug.getValue(val);
          switch (val) {
            case 0:
              sa.magFilter = GL_NEAREST;
              break;
            case 1:
              sa.magFilter = GL_LINEAR;
              break;
            default:
              //this should be impossible
              sa.magFilter = GL_LINEAR;
              break;
          };
        }
        if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
			error.format(error, name + "MagFilter");
			MGlobal::displayError(error);
			MGlobal::displayError(stat.errorString());
        }
        if ( !dn.hasAttribute( name+"Wrap" )) {
          sa.wrapAttrib = eAttrib.create( name+"Wrap", name+"Wrap");
          eAttrib.addField( "Repeat", 0);
          eAttrib.addField( "Clamp", 1);
          eAttrib.addField( "Mirror", 2);
          eAttrib.setDefault( 0);
          eAttrib.setInternal(true);
          
          stat = mod.addAttribute( thisMObject(), sa.wrapAttrib);
        }
        else {
          sa.wrapAttrib = dn.attribute(name+"Wrap", &stat);
          //need to query the filter attribute
		  MPlug plug(thisMObject(), sa.wrapAttrib);
          short val;
          plug.getValue(val);
          switch (val) {
            case 0:
              sa.wrapMode = GL_REPEAT;
              break;
            case 1:
              sa.wrapMode = GL_CLAMP_TO_EDGE;
              break;
            case 2:
              sa.wrapMode = GL_MIRRORED_REPEAT;
              break;
            default:
              //this should be impossible
              sa.wrapMode = GL_REPEAT;
              break;
          };
        }
        if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
			error.format(error, name + "Wrap");
			MGlobal::displayError(error);
			MGlobal::displayError(stat.errorString());
        }
        if ( !dn.hasAttribute( name+"MaxAniso" )) {
          sa.anisoAttrib = nAttrib.create( name+"MaxAniso", name+"MaxAniso", MFnNumericData::kFloat, 1.0);
          
          nAttrib.setInternal(true);
          nAttrib.setMin( 1.0);
          nAttrib.setMax( 16.0);
          stat = mod.addAttribute( thisMObject(), sa.anisoAttrib);
        }
        else {
          sa.anisoAttrib = dn.attribute(name+"MaxAniso", &stat);
          //need to query the filter attribute
		  MPlug plug(thisMObject(), sa.anisoAttrib);
          float val;
          plug.getValue(val);
          sa.maxAniso = val;
        }
        if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedAddAttribute, strStat);
			error.format(error, name + "MaxAniso");
			MGlobal::displayError(error);
			MGlobal::displayError(stat.errorString());
        }
        m_samplerAttribList.push_back(sa);
        GL_CHECK;
      }
    }

    //attributes
    for (ii=0; ii<m_shader->attributeCount(); ii++) {
      MString name(m_shader->attributeName(ii));

      //iterate the list to find it
      std::vector<attributeAttrib>::iterator it=m_attributeAttribList.begin();

      //might want to store this stuff in a map instead
      while (it < m_attributeAttribList.end()) {
        if (it->name == name)
          break; //found it
        
        it++; // look at the next
      }

      if (it !=m_attributeAttribList.end()) {
        //if found, set the pNum to point to this one
        it->pNum = ii;
        it->handle = m_shader->attributeHandle( ii);
        it->set = -1;
      }
      else {
        MFnTypedAttribute tAttrib;
        attributeAttrib aa;
        MStatus stat;

        aa.name = name;
        aa.setName = name;
        aa.pNum = ii;
        aa.handle = m_shader->attributeHandle(ii);
        aa.set = -1; //no set
		aa.mtype = kNonMaya;

        if ( !dn.hasAttribute( name )) {
          aa.attrib = tAttrib.create( name, name, MFnData::kString);
          
          tAttrib.setInternal(true);
          tAttrib.setKeyable( true );
          stat = mod.addAttribute( thisMObject(), aa.attrib);
        }
        else {
          aa.attrib = dn.attribute(name, &stat);
        }
        m_attributeAttribList.push_back(aa);
      }
    }
  }

  for (ii=0; ii< (int)m_uniformAttribList.size(); ) {
    if (m_uniformAttribList[ii].pNum == -1) {

      //attrib is unused, remove it
      if (m_uniformAttribList[ii].attrib != MObject::kNullObj ) {
        mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib);
        m_uniformAttribList[ii].attrib = MObject::kNullObj;
      }

      if ( m_uniformAttribList[ii].attrib2 != MObject::kNullObj ) {
        mod.removeAttribute( thisMObject(), m_uniformAttribList[ii].attrib2);
        m_uniformAttribList[ii].attrib2 = MObject::kNullObj;
      }
      
      m_uniformAttribList.erase(m_uniformAttribList.begin()+ii);
    }
    else {
      //move to the next item
      ii++;
    }
  }

  for (ii=0; ii<(int)m_samplerAttribList.size(); ) {
    MStatus stat, stat2, stat3, stat4, stat5;
    if (m_samplerAttribList[ii].pNum == -1) {
      //attrib is unused, disconnect it and remove it
      
      if ( m_samplerAttribList[ii].attrib != MObject::kNullObj )
        stat = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].attrib);

      if ( m_samplerAttribList[ii].magFilterAttrib != MObject::kNullObj )
        stat2 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].magFilterAttrib);

      if ( m_samplerAttribList[ii].minFilterAttrib != MObject::kNullObj )
        stat3 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].minFilterAttrib);

      if ( m_samplerAttribList[ii].wrapAttrib != MObject::kNullObj )
        stat4 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].wrapAttrib);

      if ( m_samplerAttribList[ii].anisoAttrib != MObject::kNullObj )
        stat5 = mod.removeAttribute( thisMObject(), m_samplerAttribList[ii].anisoAttrib);

      if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedDestroyAttribute, strStat);
			error.format(error, m_samplerAttribList[ii].name);
			MGlobal::displayError(error);
			MGlobal::displayError(MString("  ") + stat.errorString());
      }

      //I don't think we have a context for certain here
      ResourceManager::destroyTexture( m_samplerAttribList[ii].texName);
      
      m_samplerAttribList[ii].attrib = MObject::kNullObj;
      m_samplerAttribList.erase(m_samplerAttribList.begin()+ii);
    }
    else {
      //move to the next item
      ii++;
    }
  }

  for (ii=0; ii<(int)m_attributeAttribList.size(); ) {
    MStatus stat;
    if (m_attributeAttribList[ii].pNum == -1) {
      //attrib is unused, disconnect it and remove it
      
      stat = mod.removeAttribute( thisMObject(), m_attributeAttribList[ii].attrib);
      if (stat != MStatus::kSuccess) {
			MStatus strStat;
			MString error = MStringResource::getString(rGLSLShaderNodeFailedDestroyAttribute, strStat);
			error.format(error, m_attributeAttribList[ii].name);
			MGlobal::displayError(MString("  ") + stat.errorString());
      }

      m_attributeAttribList[ii].attrib = MObject::kNullObj;
      m_attributeAttribList.erase(m_attributeAttribList.begin()+ii);
    }
    else {
      //move to the next item
      ii++;
    }
  }

  GL_CHECK;

  mod.doIt();
}

//
//
//
////////////////////////////////////////////////////////////////////////////////
bool glslShaderNode::locateFile( const MString &name, MString &path) {
  MString proj;
  struct stat fstats;
  MString temp;

  // 
  // I swear, there must be a better way
  MGlobal::executeCommand( MString("workspace -q -rd;"), proj);

  //search order:
  // current directory
  // project/shaders directory
  // project directory

  // Try absolute path first
  if ( stat(name.asChar(), &fstats) == 0){
    //found it in the current path
    path = name;
    return true;
  }

  // Get the current working directory and
  // try relative path
  char buffer[MAX_PATH_LENGTH];
  if ( getcwd( buffer, MAX_PATH_LENGTH ) != NULL)
  {
	  temp = MString(buffer) + "\\" + name;
	  if ( stat(temp.asChar(), &fstats) == 0){
		  //found it in the current path
		  path = temp;
		  return true;
	  }
  }

  temp = proj + "shaders/" + name;
  if ( stat(temp.asChar(), &fstats) == 0){
    //found it in the project/shaders path
    path = temp;
    return true;
  }
 
  temp = proj + name;
  if ( stat(temp.asChar(), &fstats) == 0){
    //found it in the project path
    path = temp;
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////
// Swatch rendering
////////////////////////////////////////////////////////////////////////

/* virtual */
MStatus glslShaderNode::renderSwatchImage( MImage & outImage )
{
	MStatus status = MStatus::kFailure;

	// Get the hardware renderer utility class
	MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
	if (pRenderer)
	{
		const MString& backEndStr = pRenderer->backEndString();

		// Get geometry
		// ============
		unsigned int* pIndexing = 0;
		unsigned int  numberOfData = 0;
		unsigned int  indexCount = 0;

		MHardwareRenderer::GeometricShape gshape = MHardwareRenderer::kDefaultSphere;
		MGeometryData* pGeomData =
			pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount );
		if( !pGeomData )
		{
			return MStatus::kFailure;
		}

		// Make the swatch context current
		// ===============================
		//
		unsigned int width, height;
		outImage.getSize( width, height );
		unsigned int origWidth = width;
		unsigned int origHeight = height;

		MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr, width, height );

		if( status2 != MS::kSuccess )
		{
			pRenderer->dereferenceGeometry( pGeomData, numberOfData );
            return status2;
		}

		// Get the light direction from the API, and use it
		// =============================================
		{
			float light_pos[4];
			pRenderer->getSwatchLightDirection( light_pos[0], light_pos[1], light_pos[2], light_pos[3] );

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadIdentity();
			glLightfv(GL_LIGHT0, GL_POSITION, light_pos );
			glPopMatrix();

			float light_ambt[4] = {1.0f, 1.0f, 1.0f, 1.0};
			float light_diff[4] = {1.0f, 1.0f, 1.0f, 1.0};
			float light_spec[4] = {1.0f, 1.0f, 1.0f, 1.0};

			glLightfv( GL_LIGHT0, GL_AMBIENT,  light_ambt );
			glLightfv( GL_LIGHT0, GL_DIFFUSE,  light_diff );
			glLightfv( GL_LIGHT0, GL_SPECULAR, light_spec );

			glEnable( GL_LIGHTING );
			glEnable( GL_LIGHT0 );

            GL_CHECK;
		}

		// Get camera
		// ==========
		{
			// Get the camera frustum from the API
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			double l, r, b, t, n, f;
			pRenderer->getSwatchPerspectiveCameraSetting( l, r, b, t, n, f );
			glFrustum( l, r, b, t, n, f );

			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
			float x, y, z, w;
			pRenderer->getSwatchPerspectiveCameraTranslation( x, y, z, w );
			glTranslatef( x, y, z );

            GL_CHECK;
		}

		// Get the default background color and clear the background
		//
		float r, g, b, a;
		MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a );
		glClearColor( r, g, b, a );
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glShadeModel(GL_SMOOTH);
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

        GL_CHECK;

		// Draw The Swatch
		// ===============
		//drawTheSwatch( pGeomData, pIndexing, numberOfData, indexCount );
		MDagPath dummyPath;
		glBind( dummyPath );

		float *vertexData = (float *)( pGeomData[0].data() );
		float *normalData = (float *)( pGeomData[1].data() );
		float *uvData = (float *)( pGeomData[2].data() );
		float *tangentData = (float *)( pGeomData[3].data() );
		float *binormalData = (float *)( pGeomData[4].data() );
		unsigned int normalCount = 0;

		// Stick normal, tangent, binormals into ptr array
		float ** normalArrays = new float * [3];
		if (normalData) 
		{
			normalArrays[0] = normalData;
			normalCount++;
		}
		else
			normalArrays[0] = NULL;
		if (tangentData) 
		{
			normalArrays[1] = tangentData;
			normalCount++;
		}
		else
			normalArrays[1] = NULL;
		if (binormalData)
		{
			normalArrays[2] = binormalData;
			normalCount++;
		}
		else
			normalArrays[2] = NULL;

		// Stick uv data into ptr array
		unsigned int uvCount = 0;
		float ** texCoordArrays = new float * [1];
		if (uvData)
		{
			texCoordArrays[0] = uvData;
			uvCount = 1;
		}
		else
			texCoordArrays[0] = NULL;

		m_setNums[0] = 0;
		m_setNums[1] = 0;
		m_setNums[2] = 0;
		m_setNums[3] = 0;
		m_setNums[4] = 0;
		m_setNums[5] = 0;
		m_setNums[6] = 0;
		m_setNums[7] = 0;

		m_mayaType[0] = kUvSet;
		m_mayaType[1] = kTangent;
		m_mayaType[2] = kBinormal;
		m_mayaType[3] = kUvSet;
		m_mayaType[4] = kUvSet;
		m_mayaType[5] = kUvSet;
		m_mayaType[6] = kUvSet;
		m_mayaType[7] = kUvSet;

		// No colours
		m_colorSetNums[0] = -1;
		m_colorSetNums[1] = -1;
		m_colorMayaType[0] = kColorSet;
		m_colorMayaType[1] = kColorSet;

		glGeometry( dummyPath,
					GL_TRIANGLES,
					false,
					indexCount,
					pIndexing,
					0, 
					NULL, /* no vertex ids */
					vertexData,
					normalCount,
					(const float **) normalArrays,
					0, 
					NULL, /* no colours */
					uvCount,
					(const float **) texCoordArrays);


		glUnbind( dummyPath );
		glFinish();

		normalArrays[0] = NULL;
		normalArrays[1] = NULL;
		delete[] normalArrays;
		texCoordArrays[0] = NULL;
		delete[] texCoordArrays;

		// Read pixels back from swatch context to MImage
		// ==============================================
		pRenderer->readSwatchContextPixels( backEndStr, outImage );

		// Double check the outing going image size as image resizing
		// was required to properly read from the swatch context
		outImage.getSize( width, height );
		if (width != origWidth || height != origHeight)
		{
			status = MStatus::kFailure;
		}
		else
		{
			status = MStatus::kSuccess;
		}
	}
    GL_CHECK;
	return status;
}

//
//
////////////////////////////////////////////////////////////////////////////////
bool glslShaderNode::printVertexShader( int pass) {

  if ( m_shader) {
    const char *shader = m_shader->getVertexShader( pass);
    if (shader) {
      MGlobal::displayInfo(shader);
      return true;
    }
  }

  return false;
}

//
//
////////////////////////////////////////////////////////////////////////////////
bool glslShaderNode::printPixelShader( int pass) {
  if ( m_shader) {
    const char *shader = m_shader->getPixelShader( pass);
    if (shader) {
      MGlobal::displayInfo(shader);
      return true;
    }
  }

  return false;
}

//
//
////////////////////////////////////////////////////////////////////////////////
glslShaderNode* glslShaderNode::findNodeByName(const MString &name) {
  for (std::vector<glslShaderNode*>::iterator it = sNodeList.begin(); it < sNodeList.end(); it++) {
    if ( (*it)->name() == name)
      return *it;
  }

  return NULL;
}

